在口語上我們常將函式的 argument (引數)和 parameter (參數)互相替換使用,然而在程式開發裡二者各有自己的定義。
function askSiri(action){
return assist("Hey Siri", action);
}
var askGoogle = function(action) {
return assist("Ok Google", action);
};
var assist = (assistant, action) => assistant + ", " + action;
askSiri("make me a sandwich");
askGoogle("tell Siri to make me a sandwich");
屬於 parameter 的有:
askSiri
括號裡的action
askGoogle
括號裡的action
assist
括號裡的assistant
和action
屬於 argument 的有:
assist
函式裡的Hey Siri
, Ok Google
以及action
askSiri
函式裡的"make me a sandwich"
askGoogle
函式裡的"tell Siri to make me a sandwich"
由此我們可以發現,parameter 是在函式的定義中指定的,代表即將代入函式的實值。而 argument 則是在呼叫函式時要傳遞給函式的值。
一般情況我們會預期 argument 和 parameter 的數量相等,然後按照順序指派,第一個 argument 指派給第一個 parameter, 第二個 argument 指派給第二個 parameter, 依此類推。
當二者的數量不等時 JavaScript 也有一套處理原則,如果 argument 比 parameter 多,多餘的 argument 並沒有什麼作用。
如果 parameter 比 argument 多,多出來的 parameter 其值會設為undefined
。
function moneyPaidToApple(iPhone, iPad, mac, appleWatch, appleTV) { ... }
moneyPaidToApple("iPhone Xs Max", "iPad Pro 12.9", "MacBook Pro 15", "Apple Watch Series 4", "Apple TV 4k", "AirPods");
moneyPaidToApple("iPhone XR");
範例中第一個函式呼叫 parameter 多於 argument,多出來的”AirPods”
沒什麼用,有寫跟沒寫一樣。
第二個函式呼叫 parameter 只有一項,在函式內的iPad
,mac
,appleWatch
,appleTV
arguments 的值都會是undefined
。
Rest parameter 是 ES6 才加入 JavaScript 標準的功能,它能讓我們在定義函式時,用一個 parameter 代表一連串的 arguments。Rest parameter 會以陣列代表這些 arguments,我們就可以用陣列方法處理這些值。
Rest parameter 一定位於 parameter 的最後一個,在所有 arguments 照順序指派給對應的 parameters 後,剩下來的 arguments 就會全部包含在 rest parameter,形成一個陣列。
假設有一個手機行要舉辦促銷活動,凡是購買手機超過 15000 的話,所有配件打九折;手機價格超過 25000 的話,配件打八折。因為我們無法預估客人會買多少樣配件,這時 rest parameter 就派上用場。
function sale(phone, ...accessories){
var accessoriesTotalPrice = 0;
var discount = 1;
var phonePrice = getPhonePrice(phone);
for(var i = 0; i < accessories.length; i++){
accessoriesTotalPrice += getAccessoryPrice(accessories[i]);
}
if (phonePrice >= 25000) {
discount = 0.8;
else if (phonePrice >= 15000) {
discount = 0.9;
}
return phonePrice + accessoriesTotalPrice * discount;
}
然後就可以把客人購買的商店直接代入這個函式來計算出總金額。
var craig = sale("iPhone Xs", "AirPods", "矽膠保護殻", "螢幕保護貼");
var paul = sale("Google Pixel 3a", "螢幕保護貼", "無線充電盤");
沒有 rest parameter 的話,配件部份需要額外先處理成一個陣列,使用上就多一道手續。
Default parameter 也是 ES6 新加入的標準,它讓我們在定義函式時先指定 parameter 的預設值,在呼叫函數時如果這個 parameter 沒有被指派到 argument 的值,就會以預設值代入。這讓我們省了不少開發時的麻煩!
回到前面手機行的例子,假設我們的網路商店預設以宅配出貨,但是客人也可以選擇其他的送貨方式
function order(price, shipping = "宅配") {
var total = price + getShippingPrice(shipping);
console.log("本訂單將以" + shipping + "方式出貨,總金額為" + total + "元");
}
於是我們得到客人的訂單總金額。
// 此訂單會以預設的方式出貨
var craig = order(25100);
// 這些訂單以客人選擇的方式出貨
var paul = order(14536, "到店取貨");
var david = order(138, "超商付款取貨");
沒有 default parameter 的話,需要多加一道判斷式,如果參數不是undefined
,就使用代入的值,否則就設為預設值。
function order(price, shipping) {
if(typeof shipping === "undefined") {
shipping = "宅配";
}
var total = price + getShippingPrice(shipping);
console.log("本訂單將以" + shipping + "方式出貨,總金額為" + total + "元");
}
少寫程式碼總是讓人比較開心。
在呼叫函式時,其實 JavaScript 函式隱藏了一個秘技,就是函式自帶二個參數,我們不用先定義。這二個參數一個是arguments
,另一個是this
,我們先來看看arguments
。
從字面上就看得出來arguments
跟呼叫函式時代入的參數有關,事實上它是參數的集合,以「類陣列」的結構呈現,它具有.length
屬性,以及可以用 index number 取得項目值,但是無法在arguments
使用陣列方法,換句話說arguments.sort
,arguments.map
這些方法是不能用的。
function foo(a, b, c) {
console.log("a === 1", a === 1); // true
console.log("b === 2", b === 2); // true
console.log("c === 3", c === 3); // true
console.log(arguments.length); // 5
console.log("arguments[0] === a", arguments[0] === a); // true
console.log("arguments[1] === b", arguments[1] === b); // true
console.log("arguments[2] === c", arguments[2] === c); // true
console.log("arguments[3] === 4", arguments[3] === 4); // true
console.log("arguments[4] === 5", arguments[4] === 5); // true
}
foo(1, 2, 3, 4, 5);
前面提過如果 argument 沒有相對應的 parameter 就不會被指派,但是仍然可以透過arguments
隱含參數取得。
這樣子看起來arguments
似乎跟 rest parameter 功能很像?是的,在沒有 rest parameter 之前 JavaScript 開發者會運用arguments
處理不固定數量的參數,不過現在我們有了 rest parameter,在瀏覽器可以支援情況下,利用身為陣列的 rest parameter 還是會比較方便。